﻿using System;
using System.Collections.Generic;
using System.Text;

namespace JustMathCore.FilePool
{
    public class FileItem : IDisposable
    {
        byte[] header;
        int linesize;
        System.IO.Stream filestream;
        FileItem[] subitems;

        public string HeaderName
        {
            get;
            private set;

        }
        public string Path
        {
            get;
            private set;
        }
        public uint curFileLineCount
        {
            get;
            private set;
        }
        byte curFileIndex;
        uint curLineCount;
        uint FileLineCountMax;
        public FileItem(string outdir, byte[] header, int linesize)
        {
            this.header = header;

            var headerpath = header[0].ToString("X02");
            for (var i = 1; i < header.Length; i++)
            {
                headerpath = System.IO.Path.Combine(headerpath, header[i].ToString("X02"));
            }
            this.HeaderName = headerpath;

            this.Path = System.IO.Path.Combine(outdir, HeaderName);


            this.linesize = linesize;

            if (System.IO.Directory.Exists(Path) == false)
                System.IO.Directory.CreateDirectory(Path);

            //单文件最大行数1G
            this.FileLineCountMax = (uint)((1 * 1024 * 1024 * 1024 - 8) / linesize);

            byte[] data = new byte[4];


            for (var c = 0; c < 256; c++)
            {
                curFileIndex = (byte)c;

                var filename = System.IO.Path.Combine(this.Path, HeaderName + "_" + c.ToString("x02") + ".bin");
                if (System.IO.File.Exists(filename))
                {
                    this.filestream = System.IO.File.Open(filename, System.IO.FileMode.Open);
                    this.filestream.Read(data, 0, 4);
                    curFileLineCount = BitConverter.ToUInt32(data, 0);
                    this.filestream.Read(data, 0, 4);
                    curLineCount = BitConverter.ToUInt32(data, 0);
                    if (curLineCount < curFileLineCount)
                        break;
                    else if (curFileLineCount < FileLineCountMax)
                    {
                        ExtCurFile();
                    }
                    else
                    {
                        this.filestream.Close();
                        this.filestream = null;
                        continue;
                    }
                }
                else if (InitNextFile((byte)c))
                {
                    break;
                }
                else
                {
                    throw new Exception("error file.");
                }
            }
            if (this.filestream == null)
                throw new Exception("open fileitem error:" + Path);

            buf = new byte[linesize * 4096];
            bufsize = 0;
        }
        void ExtCurFile()
        {
            //扩充文件容量
            var extdata = new byte[8 * 1024 * 1024];
            this.filestream.Seek(0, System.IO.SeekOrigin.End);
            this.filestream.Write(extdata, 0, extdata.Length);
            curFileLineCount = (uint)((this.filestream.Position - 8) / linesize);
            var data = BitConverter.GetBytes(curFileLineCount);
            this.filestream.Seek(0, System.IO.SeekOrigin.Begin);
            this.filestream.Write(data, 0, 4);
        }
        public bool InitNextFile(byte FileIndex)
        {
            if (this.filestream != null)
            {
                this.filestream.Close();
                this.filestream = null;
            }
            var filename = System.IO.Path.Combine(this.Path, HeaderName + "_" + FileIndex.ToString("x02") + ".bin");
            if (System.IO.File.Exists(filename))
                return false;

            this.filestream = System.IO.File.Open(filename, System.IO.FileMode.Create);

            //默认8M空间
            var wdata = new byte[8 * 1024 * 1024];
            this.curFileLineCount =(uint)( (wdata.Length - 8) / this.linesize);

            var max = BitConverter.GetBytes(curFileLineCount);
            wdata[0] = max[0];
            wdata[1] = max[1];
            wdata[2] = max[2];
            wdata[3] = max[3];

            this.filestream.Write(wdata, 0, wdata.Length);
            curFileIndex = FileIndex;
            curLineCount = 0;
            return true;

        }

        public byte[] buf;
        public uint bufsize;
        public void WriteLine(byte[] byteline)
        {

            var bi = bufsize * linesize;
            for (var i = 0; i < linesize; i++)
            {
                buf[bi + i] = byteline[i];
            }
            bufsize++;
            if (bufsize == 4096 || (bufsize + curLineCount >= curFileLineCount))
                Apply();

        }

        public void Apply()
        {


            var indexforline = curLineCount * linesize + 8;
            this.filestream.Seek(indexforline, System.IO.SeekOrigin.Begin);
            this.filestream.Write(buf, 0, (int)(linesize * bufsize));

            //前4个字节 max
            //然后4个字节当前数
            this.filestream.Seek(4, System.IO.SeekOrigin.Begin);

            curLineCount += bufsize;
            bufsize = 0;
            var curcountdata = BitConverter.GetBytes(curLineCount);

            this.filestream.Write(curcountdata, 0, 4);

            if (curLineCount >= curFileLineCount)

            {
                if (curFileLineCount < FileLineCountMax)
                {
                    ExtCurFile();
                }
                else
                {
                    //最后一次
                    var b = InitNextFile((byte)(curFileIndex + 1));
                    if (!b)
                        throw new Exception("error next file");
                }
            }
        }
        public void Dispose()
        {
            filestream.Close();
        }
    }
    public class LinePool : IDisposable
    {
        //level=1
        int linesize;
        string outpath;
        public LinePool(string outdir, int linesize = 32)
        {
            if (System.IO.Directory.Exists(outdir) == false)
                System.IO.Directory.CreateDirectory(outdir);

            this.outpath = outdir;
            this.linesize = linesize;
        }

        Dictionary<byte, FileItem> fileItems = new Dictionary<byte, FileItem>();
        //根据第n个字节分组
        public FileItem OpenOrCreate(byte head)
        {
            if (fileItems.TryGetValue(head, out FileItem item))
            {
                return item;
            }
            else
            {
                var _item = new FileItem(outpath, new byte[1] { head }, linesize);
                fileItems[head] = _item;
                return _item;
            }
        }
        public void WriteLine(byte[] byteline)
        {
            if (byteline.Length != this.linesize)
                throw new Exception("error data Length");
            FileItem item = OpenOrCreate(byteline[0]);
            item.WriteLine(byteline);
        }

        public void Dispose()
        {
            foreach (var f in fileItems)
            {
                f.Value.Dispose();
            }
            fileItems.Clear();
        }
    }
}
